﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Windows.Markup;
using System.Xml.Linq;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using System.Windows;
using System.Globalization;
using System.Windows.Media;
using System.Threading;
using System.Web.UI;

namespace VS.Web
{
    /// <summary>
    /// Summary description for ShowAlternate
    /// </summary>
    public class ShowAlternate : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/html";

            string article = HttpContext.Current.Server.MapPath(context.Request.QueryString["article"]);
            string path = Path.Combine(Path.GetDirectoryName(HttpContext.Current.Server.MapPath(context.Request.CurrentExecutionFilePath)), "bin\\Content");
            string xaml = path + "\\" + context.Request.QueryString["article"] + ".xaml";
            StringBuilder sb = new StringBuilder();
            using (TextReader tr = new StreamReader(xaml))
            {
                ContainerNode content = new ContainerNode(XElement.Parse(tr.ReadToEnd()));
                XElement html = new XElement("html");
                XElement head = new XElement("head");
                XElement script1 = new XElement("script", new XAttribute("src", "Silverlight.js"), new XAttribute("type", "text/javascript"));
                XElement script2 = new XElement("script", new XAttribute("type", "text/javascript"), string.Format("if Silverlight.isInstalled() window.location='main/main/article/={0}';", article));
                XElement body = new XElement("body");

                head.Add(script1);
                html.Add(head, body);
                body.Add(script2, content.HtmlElement);
                
                context.Response.Write(html.ToString());
            }
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }

    public interface INode
    {
        INode Parent { get; }
        XElement HtmlElement { get; }
        int Row { get; }
        int Col { get; }
        int RowSpan { get; }
        int ColSpan { get; }
        bool HasStyleAttributes { get; }
        bool HasPositionAttributes { get; }
        bool HasCommandAttributes { get; }

    }

    public static class HtmlElements
    {
        public const string StyleAttributes = "font-size:{0}pt;font-style:{1};font-weight:{2};color:{3}";
        public const string NeutralElementName = "span";
        public const string ContainerElementName = "div";
    }

    public static class XamlElements
    {
        public const string GridElementName = "Grid";
        public const string ColumnDefinitionsElementName = "Grid.ColumnDefinitions";
        public const string ColumnDefinitionElementName = "ColumnDefinition";
        public const string RowDefinitionsElementName = "Grid.RowDefinitions";
        public const string RowDefinitionElementName = "RowDefinition";
        public const string ParagraphElementName = "Paragraph";
        public const string RunElementName = "Run";
        public const string HyperlinkElementName = "Hyperlink";
        public const string ImageElementName = "Image";
    }

    public abstract class BaseNode : INode
    {
        public INode Parent { get; private set; }
        public string Id { get; private set; }
        public XElement XamlElement { get; private set; }
        public int Row { get; private set; }
        public int Col { get; private set; }
        public int RowSpan { get; private set; }
        public int ColSpan { get; private set; }
        public FontStyle FontStyle { get; private set; }
        public double FontSize { get; private set; }
        public FontWeight FontWeight { get; private set; }
        public Color Foreground { get; private set; }
        public string Text { get; private set; }
        public string Command { get; private set; }
        public string CommandParameter { get; private set; }
        public bool HasStyleAttributes { get; private set; }
        public bool HasPositionAttributes { get; private set; }
        public bool HasCommandAttributes { get; private set; }

        public BaseNode(XElement xamlElement, INode parent, string id = null)
        {
            XamlElement = xamlElement;
            Parent = parent;
            Id = id ?? string.Empty;

            int row = 0;
            int col = 0;
            int rowspan = 1;
            int colspan = 1;
            double size = 11;
            FontWeight weight = FontWeights.Normal;
            FontStyle style = FontStyles.Normal;
            Foreground = Colors.Black;
            foreach (XAttribute attr in xamlElement.Attributes())
            {
                if (attr.Name == "Grid.Row")
                {
                    int.TryParse(attr.Value, out row);
                    HasPositionAttributes = true;
                }
                if (attr.Name == "Grid.Col")
                {
                    int.TryParse(attr.Value, out col);
                    HasPositionAttributes = true;
                }
                if (attr.Name == "Grid.RowSpan")
                {
                    int.TryParse(attr.Value, out rowspan);
                    HasPositionAttributes = true;
                }
                if (attr.Name == "Grid.ColSpan")
                {
                    int.TryParse(attr.Value, out colspan);
                    HasPositionAttributes = true;
                }
                if (attr.Name == "FontStyle")
                {
                    FontStyleStringToKnownStyle(attr.Value, ref style);
                    HasStyleAttributes = true;
                }
                if (attr.Name == "FontSize")
                {
                    double.TryParse(attr.Value, out size);
                    HasStyleAttributes = true;
                }
                if (attr.Name == "FontWeight")
                {
                    FontWeightStringToKnownWeight(attr.Value, ref weight);
                    HasStyleAttributes = true;
                }
                if (attr.Name == "Foreground")
                {
                    ColorConverter cc = new ColorConverter();
                    Foreground = (Color)ColorConverter.ConvertFromString(attr.Value);
                    HasStyleAttributes = true;
                }
                if (attr.Name == "Text")
                {
                    Text = attr.Value;
                }
                if (attr.Name == "Command")
                {
                    Command = attr.Value;
                    HasCommandAttributes = true;
                }
                if (attr.Name == "CommandParameter")
                {
                    CommandParameter = attr.Value;
                    HasCommandAttributes = true;
                }
            }

            Row = row;
            Col = col;
            RowSpan = rowspan;
            ColSpan = colspan;
            FontSize = size;
            FontStyle = style;
            FontWeight = weight;
        }

        public abstract XElement HtmlElement { get; }

        public virtual XElement ConvertToHtmlElement(Dictionary<string, string> conversion, string defaultElementName = HtmlElements.NeutralElementName)
        {
            string elementName = conversion.ContainsKey(XamlElement.Name.LocalName) ? conversion[XamlElement.Name.LocalName] : defaultElementName;
            XElement element = new XElement(elementName);

            if (HasStyleAttributes)
            {
                ColorConverter cc = new ColorConverter();
                string color = cc.ConvertToString(Foreground);
                color = string.Format("#{0}", color.Substring(3));
                element.Add(new XAttribute("style", string.Format(HtmlElements.StyleAttributes, FontSize, FontStyle, FontWeight, color)));
            }

            if (HasCommandAttributes && XamlElement.Name.LocalName == XamlElements.HyperlinkElementName)
            {
                string href = conversion.ContainsKey(Command) ? conversion[Command] + CommandParameter : string.Empty;
                string target = "_blank";
                element.Add(new XAttribute("target", target));
                element.Add(new XAttribute("href", href));
            }

            return element;
        }

        private static bool FontStyleStringToKnownStyle(string s, ref FontStyle fontStyle)
        {
            if (s.Equals("Normal", StringComparison.OrdinalIgnoreCase))
            {
                fontStyle = FontStyles.Normal;
                return true;
            }
            if (s.Equals("Italic", StringComparison.OrdinalIgnoreCase))
            {
                fontStyle = FontStyles.Italic;
                return true;
            }
            if (s.Equals("Oblique", StringComparison.OrdinalIgnoreCase))
            {
                fontStyle = FontStyles.Oblique;
                return true;
            }
            return false;
        }

        internal static bool FontWeightStringToKnownWeight(string s, ref FontWeight fontWeight)
        {
            int num;
            switch (s.Length)
            {
                case 4:
                    if (!s.Equals("Bold", StringComparison.OrdinalIgnoreCase))
                    {
                        if (!s.Equals("Thin", StringComparison.OrdinalIgnoreCase))
                        {
                            break;
                        }
                        fontWeight = FontWeights.Thin;
                        return true;
                    }
                    fontWeight = FontWeights.Bold;
                    return true;

                case 5:
                    if (!s.Equals("Black", StringComparison.OrdinalIgnoreCase))
                    {
                        if (s.Equals("Light", StringComparison.OrdinalIgnoreCase))
                        {
                            fontWeight = FontWeights.Light;
                            return true;
                        }
                        if (!s.Equals("Heavy", StringComparison.OrdinalIgnoreCase))
                        {
                            break;
                        }
                        fontWeight = FontWeights.Heavy;
                        return true;
                    }
                    fontWeight = FontWeights.Black;
                    return true;

                case 6:
                    if (!s.Equals("Normal", StringComparison.OrdinalIgnoreCase))
                    {
                        if (!s.Equals("Medium", StringComparison.OrdinalIgnoreCase))
                        {
                            break;
                        }
                        fontWeight = FontWeights.Medium;
                        return true;
                    }
                    fontWeight = FontWeights.Normal;
                    return true;

                case 7:
                    if (!s.Equals("Regular", StringComparison.OrdinalIgnoreCase))
                    {
                        break;
                    }
                    fontWeight = FontWeights.Regular;
                    return true;

                case 8:
                    if (!s.Equals("SemiBold", StringComparison.OrdinalIgnoreCase))
                    {
                        if (!s.Equals("DemiBold", StringComparison.OrdinalIgnoreCase))
                        {
                            break;
                        }
                        fontWeight = FontWeights.DemiBold;
                        return true;
                    }
                    fontWeight = FontWeights.SemiBold;
                    return true;

                case 9:
                    if (!s.Equals("ExtraBold", StringComparison.OrdinalIgnoreCase))
                    {
                        if (!s.Equals("UltraBold", StringComparison.OrdinalIgnoreCase))
                        {
                            break;
                        }
                        fontWeight = FontWeights.UltraBold;
                        return true;
                    }
                    fontWeight = FontWeights.ExtraBold;
                    return true;

                case 10:
                    if (!s.Equals("ExtraLight", StringComparison.OrdinalIgnoreCase))
                    {
                        if (s.Equals("UltraLight", StringComparison.OrdinalIgnoreCase))
                        {
                            fontWeight = FontWeights.UltraLight;
                            return true;
                        }
                        if (s.Equals("ExtraBlack", StringComparison.OrdinalIgnoreCase))
                        {
                            fontWeight = FontWeights.ExtraBlack;
                            return true;
                        }
                        if (s.Equals("UltraBlack", StringComparison.OrdinalIgnoreCase))
                        {
                            fontWeight = FontWeights.UltraBlack;
                            return true;
                        }
                        break;
                    }
                    fontWeight = FontWeights.ExtraLight;
                    return true;
            }
            if (int.TryParse(s, NumberStyles.Integer, CultureInfo.InstalledUICulture, out num))
            {
                fontWeight = FontWeight.FromOpenTypeWeight(num);
                return true;
            }
            return false;
        }
    }

    public class LeafNode : BaseNode
    {
        public LeafNode(XElement xamlElement, INode parent)
            : base(xamlElement, parent)
        {

        }

        public override XElement HtmlElement
        {
            get
            {
                Dictionary<string, string> conversion = new Dictionary<string, string>()
                {
                    {"Paragraph", "p"},
                    {"LineBreak", "br"},
                    {"Hyperlink", "a"},
                    {"Italic", "i"},
                    {"Bold", "b"},
                    {"Image", "img"},
                    {"{Binding ViewModel.DownloadFileCommand}", "DownloadFile.ashx?file=Resources/"}

                };

                XElement element = ConvertToHtmlElement(conversion);
                element.Value = string.IsNullOrEmpty(Text) ? XamlElement.Value : Text;
                return element;
            }
        }
    }

    public class HyperlinkNode : LeafNode
    {
        public string NavigateUri { get; private set; }
        public string TargetName { get; private set; }
        public bool HasHyperlinkAttributes { get; private set; }

        public HyperlinkNode(XElement xamlElement, INode parent)
            : base(xamlElement, parent)
        {
            foreach (XAttribute attr in xamlElement.Attributes())
            {
                if (attr.Name == "NavigateUri")
                {
                    NavigateUri = attr.Value;
                    HasHyperlinkAttributes = true;
                }
                if (attr.Name == "TargetName")
                {
                    TargetName = attr.Value;
                    HasHyperlinkAttributes = true;
                }
            }
        }

        public override XElement ConvertToHtmlElement(Dictionary<string, string> conversion, string defaultElementName = HtmlElements.NeutralElementName)
        {
            XElement element = base.ConvertToHtmlElement(conversion, defaultElementName);

            if (HasHyperlinkAttributes)
            {
                string href = NavigateUri;
                string target = TargetName;

                element.Add(new XAttribute("target", target));
                element.Add(new XAttribute("href", href));
            }

            return element;
        }
    }

    public class ImageNode : LeafNode
    {
        public string Source { get; private set; }
        public string Tag { get; private set; }

        public ImageNode(XElement xamlElement, INode parent)
            : base(xamlElement, parent)
        {
            foreach (XAttribute attr in xamlElement.Attributes())
            {
                if (attr.Name == "Source")
                {
                    Source = attr.Value;
                }
                if (attr.Name == "Tag")
                {
                    Tag = attr.Value;
                }
            }
        }

        public override XElement ConvertToHtmlElement(Dictionary<string, string> conversion, string defaultElementName = HtmlElements.NeutralElementName)
        {
            XElement element = base.ConvertToHtmlElement(conversion, defaultElementName);

            UriBuilder ub = new UriBuilder(
                HttpContext.Current.Request.Url.Scheme,
                HttpContext.Current.Request.Url.Host,
                HttpContext.Current.Request.Url.Port,
                "Images/" + Path.GetFileName(Source));
            element.Add(new XAttribute("src", ub.ToString()));
            element.Add(new XAttribute("alt", Tag));

            return element;
        }
    }

    public abstract class CompositeNode : BaseNode
    {
        public Collection<BaseNode> Children { get; private set; }

        protected CompositeNode(XElement xamlElement, INode parent, string id = null)
            : base(xamlElement, parent, id)
        {
            Children = new Collection<BaseNode>();
        }

        protected void AddChild(XElement subElement, string id = null)
        {
            if (subElement.HasElements)
            {
                if (subElement.Name.LocalName == XamlElements.ColumnDefinitionsElementName)
                    Children.Add(new ColumnDefinitionsNode(subElement, this));
                else
                    if (subElement.Name.LocalName == XamlElements.RowDefinitionsElementName)
                        Children.Add(new RowDefinitionsNode(subElement, this));
                    else
                        Children.Add(new ContainerNode(subElement, this));
            }
            else
            {
                if (subElement.Name.LocalName == XamlElements.HyperlinkElementName)
                    Children.Add(new HyperlinkNode(subElement, this));
                else if (subElement.Name.LocalName == XamlElements.ImageElementName)
                    Children.Add(new ImageNode(subElement, this));
                else
                    Children.Add(new LeafNode(subElement, this));
            }
        }
    }

    public class ContainerNode : CompositeNode
    {
        public ContainerNode(XElement xamlElement, INode parent = null)
            : base(xamlElement, parent)
        {
            foreach (XNode subNode in xamlElement.Nodes())
            {
                switch (subNode.NodeType)
                {
                    case System.Xml.XmlNodeType.Element:
                        AddChild((XElement)subNode);
                        break;
                    case System.Xml.XmlNodeType.Text:
                        XElement text = new XElement(XamlElements.RunElementName);
                        text.Value = subNode.ToString();
                        AddChild(text);
                        break;
                }
            }
        }

        public override XElement HtmlElement
        {
            get
            {
                Dictionary<string, string> conversion = new Dictionary<string, string>()
                {
                    {"Paragraph", "div"},
                    {"ItemsControl", "ul"},
                    {"ListBoxItem", "li"},
                    {"Grid", "table"}
                };

                XElement element = ConvertToHtmlElement(conversion);

                if (XamlElement.Name.LocalName == XamlElements.GridElementName)
                {
                    List<CompositeNode> definitionNodes = Children.OfType<RowDefinitionsNode>().Cast<CompositeNode>().Concat(Children.OfType<ColumnDefinitionsNode>()).ToList();
                    IEnumerable<INode> children = Children.Except(definitionNodes);
                    List<Triplet> grid = new List<Triplet>();

                    if (definitionNodes.OfType<RowDefinitionsNode>().Count() == 0)
                    {
                        XElement rowDefinitions = new XElement(XamlElements.RowDefinitionsElementName);
                        XElement rowDefinition = new XElement(XamlElements.RowDefinitionElementName);
                        ColumnDefinitionsNode rd = new ColumnDefinitionsNode(rowDefinitions, this);
                        rd.Children.Add(new LeafNode(rowDefinition, rd));
                        definitionNodes.Add(rd);
                    }

                    if (definitionNodes.OfType<ColumnDefinitionsNode>().Count() == 0)
                    {
                        XElement columnDefinitions = new XElement(XamlElements.ColumnDefinitionsElementName);
                        XElement columnDefinition = new XElement(XamlElements.ColumnDefinitionElementName);
                        ColumnDefinitionsNode cd = new ColumnDefinitionsNode(columnDefinitions, this);
                        cd.Children.Add(new LeafNode(columnDefinition, cd));
                        definitionNodes.Add(cd);
                    }

                    int rowIndex = 0;
                    int colIndex = 0;
                    foreach (INode rowNode in definitionNodes.OfType<RowDefinitionsNode>().SelectMany(x => x.Children))
                    {
                        colIndex = 0;
                        element.Add(new XElement("td"));
                        foreach (INode colNode in definitionNodes.OfType<ColumnDefinitionsNode>().SelectMany(x => x.Children))
                        {
                            string id = string.Format("cell_{0}_{1}", rowIndex, colIndex);
                            XElement td = new XElement("td", new XAttribute("id", id));
                            element.Add(td);
                            grid.Add(new Triplet(rowIndex, colIndex, td));
                            colIndex++;
                        }
                        rowIndex++;
                    }
                    foreach (INode node in children)
                    {
                        if (node.HasPositionAttributes)
                        {
                            XElement td = grid.FirstOrDefault(x => (int)x.First == node.Row && (int)x.Second == node.Col).Third as XElement;
                            td.Add(node.HtmlElement);
                        }
                        else
                            element.Add(node.HtmlElement);
                    }
                }
                else
                    foreach (INode node in Children)
                        element.Add(node.HtmlElement);

                return element;
            }
        }
    }

    public class ColumnDefinitionsNode : ContainerNode
    {
        public ColumnDefinitionsNode(XElement xamlElement, INode parent)
            : base(xamlElement, parent)
        {

        }

        public override XElement HtmlElement
        {
            get
            {
                return new XElement(HtmlElements.NeutralElementName);
            }
        }
    }

    public class RowDefinitionsNode : ContainerNode
    {
        public RowDefinitionsNode(XElement xamlElement, INode parent)
            : base(xamlElement, parent)
        {

        }

        public override XElement HtmlElement
        {
            get
            {
                return new XElement(HtmlElements.NeutralElementName);
            }
        }
    }

}